package com.happy.qademo.websheet.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.tika.Tika;
import com.aspose.words.License;
import com.aspose.words.Document;
import com.aspose.words.FontSettings;
import com.aspose.words.SaveFormat;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.poi.ss.usermodel.*;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;


@RestController
public class OptimizedFilePreviewController {
    private static final Logger log = LoggerFactory.getLogger(OptimizedFilePreviewController.class);
    

    @Value("${fonts.path:fonts/}")
    String fontsPath;


    private File getSafeFile(String basePath, String filePath) {
        try {
            File baseDir = new File(basePath);
            File requestedFile = new File(baseDir, filePath);
            if (requestedFile.getCanonicalPath().startsWith(baseDir.getCanonicalPath())) {
                return requestedFile;
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    

    @GetMapping("/preview/**")
    public Object previewFile(HttpServletRequest request) {
        String fullPath = request.getRequestURI();
        String filePath = fullPath.substring("/preview/".length());
        try {
            filePath = URLDecoder.decode(filePath, "UTF-8");
        } catch (java.io.UnsupportedEncodingException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        String uploadPath = System.getProperty("user.dir") + File.separator + "uploads";
        File safeFile = getSafeFile(uploadPath, filePath);
        if (safeFile.isDirectory()) {
            return ResponseEntity.badRequest().body("不支持预览目录");
        }

        if (safeFile != null && safeFile.exists() && safeFile.isFile()) {
            String fileName = safeFile.getName();
            String extension = getExtension(fileName);
            try {
                switch (extension.toLowerCase()) {
                    case "docx":
                    case "doc":
                        return convertAndPreviewWord(safeFile);
                    case "xlsx":
                    case "xls":
                        return previewExcelWithLuckySheet(safeFile);
                    case "pdf":
                    case "txt":
                    case "sh":
                    case "jpg":
                    case "jpeg":
                    case "png":
                    case "gif":
                    case "html":
                    case "json":
                    case "mp4":
                        return previewRegularFile(safeFile, fileName);
                    default:
                        return ResponseEntity.badRequest().body("不支持的文件类型");
                }
            } catch (IOException e) {
                log.error("获取文件流异常", e);
                return ResponseEntity.badRequest().body("预览失败，请稍后重试");
            }
        }
        return ResponseEntity.notFound().build();
    }

    private ResponseEntity<?> convertAndPreviewWord(File wordFile) throws IOException {
        String pdfPath = convertWordToPdf(wordFile.getAbsolutePath());
        File pdfFile = new File(pdfPath);
        byte[] buf = readFileToByteArray(pdfFile);
        FileUtils.deleteFile(pdfPath);
        return getResponseEntity(buf, "inline", wordFile.getName().replaceFirst("[.][^.]+$", ".pdf"));
    }

    private ResponseEntity<?> previewExcelWithLuckySheet(File excelFile) throws IOException {
        List<Map<String, Object>> luckySheetData = convertExcelToLuckySheetData(excelFile);
        return ResponseEntity.ok(luckySheetData);
    }


    private ResponseEntity<?> previewRegularFile(File file, String fileName) throws IOException {
        byte[] buf = readFileToByteArray(file);
        return getResponseEntity(buf, "inline", fileName);
    }




    private String convertWordToPdf(String wordPath) throws IOException {
        String pdfPath = wordPath.substring(0, wordPath.lastIndexOf(".")) + ".pdf";
        try (FileOutputStream os = new FileOutputStream(pdfPath)) {

            InputStream is = OptimizedFilePreviewController.class.getClassLoader().getResourceAsStream("license.xml");
            License aposeLic = new License();
            aposeLic.setLicense(is);
            
            Document doc = new Document(wordPath);
            if (System.getProperty("os.name").toLowerCase().contains("linux")) {
                FontSettings fontSettings = new FontSettings();
                fontSettings.setFontsFolder(fontsPath, true);
                doc.setFontSettings(fontSettings);
            }
            doc.save(os, SaveFormat.PDF);
        } catch (Exception e) {
            log.error("word 转 pdf 异常", e);
            FileUtils.deleteFile(pdfPath);
            throw new IOException("文件转换失败");
        }
        return pdfPath;
    }

    private byte[] readFileToByteArray(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] buf = new byte[(int) file.length()];
            fis.read(buf);
            return buf;
        }
    }

    private ResponseEntity<?> getResponseEntity(byte[] buf, String contentDispositionType, String originalFilename) {
        ResponseEntity.BodyBuilder responseEntity = ResponseEntity.ok();
        HttpHeaders httpHeaders = new HttpHeaders();
        Tika tika = new Tika();
        String mediaType = tika.detect(buf);
        httpHeaders.setContentType(MediaType.parseMediaType(mediaType));
        httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, contentDispositionType + "; filename=" + originalFilename);
        httpHeaders.setCacheControl(CacheControl.noCache());
        return responseEntity.headers(httpHeaders).body(buf);
    }

    private String getExtension(String filename) {
        int dotIndex = filename.lastIndexOf(".");
        if (dotIndex > 0 && dotIndex < filename.length() - 1) {
            return filename.substring(dotIndex + 1);
        }
        return "";
    }

  
    
    public List<Map<String, Object>> convertExcelToLuckySheetData(File excelFile) throws IOException {
        List<Map<String, Object>> sheetsData = new ArrayList<>();

        try (FileInputStream fis = new FileInputStream(excelFile);
             Workbook workbook = new XSSFWorkbook(fis)) {

            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sheet = workbook.getSheetAt(i);
                List<Map<String, Object>> cellDataList = new ArrayList<>();

                for (Row row : sheet) {
                    for (Cell cell : row) {
                        Map<String, Object> cellData = new HashMap<>();
                        cellData.put("r", row.getRowNum());
                        cellData.put("c", cell.getColumnIndex());

                        Map<String, Object> value = new HashMap<>();
                        Map<String, Object> ct = new HashMap<>();
                        ct.put("fa", "General");
                        ct.put("t", "g");
                        value.put("ct", ct);

                        switch (cell.getCellType()) {
                            case STRING:
                                String stringValue = cell.getStringCellValue();
                                value.put("m", stringValue);
                                value.put("v", stringValue);
                                break;
                            case NUMERIC:
                                double numericValue = cell.getNumericCellValue();
                                value.put("m", String.valueOf(numericValue));
                                value.put("v", numericValue);
                                break;
                            case BOOLEAN:
                                boolean booleanValue = cell.getBooleanCellValue();
                                value.put("m", String.valueOf(booleanValue));
                                value.put("v", booleanValue);
                                break;
                            default:
                                value.put("m", "");
                                value.put("v", "");
                                break;
                        }

                        cellData.put("v", value);
                        cellDataList.add(cellData);
                    }
                }

                Map<String, Object> sheetInfo = new HashMap<>();
                sheetInfo.put("name", sheet.getSheetName());
                sheetInfo.put("color", "");
                sheetInfo.put("index", String.valueOf(i));
                sheetInfo.put("status", i == 0? "1" : "0");
                sheetInfo.put("order", String.valueOf(i));
                sheetInfo.put("hide", 0);
                sheetInfo.put("row", sheet.getLastRowNum() + 1);

                int maxColumn = 0;
                for (Row row : sheet) {
                    if (row.getLastCellNum() > maxColumn) {
                        maxColumn = row.getLastCellNum();
                    }
                }
                sheetInfo.put("column", maxColumn);

                sheetInfo.put("defaultRowHeight", 19);
                sheetInfo.put("defaultColWidth", 73);

                Map<String, Object> config = new HashMap<>();
                config.put("merge", new HashMap<>());
                config.put("rowlen", new HashMap<>());
                config.put("columnlen", new HashMap<>());
                config.put("rowhidden", new HashMap<>());
                config.put("colhidden", new HashMap<>());
                config.put("borderInfo", new HashMap<>());
                config.put("authority", new HashMap<>());
                sheetInfo.put("config", config);

                sheetInfo.put("scrollLeft", 0);
                sheetInfo.put("scrollTop", 315);
                sheetInfo.put("luckysheet_select_save", new ArrayList<>());
                sheetInfo.put("calcChain", new ArrayList<>());
                sheetInfo.put("isPivotTable", false);
                sheetInfo.put("pivotTable", new HashMap<>());
                sheetInfo.put("filter_select", new HashMap<>());
                sheetInfo.put("filter", null);
                sheetInfo.put("luckysheet_alternateformat_save", new ArrayList<>());
                sheetInfo.put("luckysheet_alternateformat_save_modelCustom", new ArrayList<>());
                sheetInfo.put("luckysheet_conditionformat_save", new HashMap<>());
                sheetInfo.put("frozen", new HashMap<>());
                sheetInfo.put("freezen", new HashMap<>());
                sheetInfo.put("chart", new ArrayList<>());
                sheetInfo.put("zoomRatio", 1);
                sheetInfo.put("image", new ArrayList<>());
                sheetInfo.put("showGridLines", 1);
                sheetInfo.put("dataVerification", new HashMap<>());

                sheetInfo.put("visibledatarow", new ArrayList<>());
                sheetInfo.put("visibledatacolumn", new ArrayList<>());
                sheetInfo.put("ch_width", 2322);
                sheetInfo.put("rh_height", 949);
                sheetInfo.put("load", "1");
                sheetInfo.put("celldata", cellDataList);
                sheetsData.add(sheetInfo);
            }
        }

        return sheetsData;
    }





}

// 文件工具类
class FileUtils {
    public static void deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
    }
   
}
